namespace Ribbons {

	public class ColorScheme : Object {

		public Color pretty_dark { get; set; }
		public Color dark { get; set; }
		public Color light_dark { get; set; }
		public Color normal { get; set; }
		public Color light_bright { get; set; }
		public Color bright { get; set; }
		public Color pretty_bright { get; set; }

		public ColorScheme () {
//			this.normal = Color.from_rgb (0.957, 0.957, 0.957);
			this.normal = Color.from_rgb (0.867, 0.867, 0.867);
//			this.normal = Color.from_rgb (0.925, 0.914, 0.847);
//			this.normal = Color.from_rgb (0.937, 0.922, 0.898);

			// XXX-GEETK
			this.pretty_dark = get_color_relative (this.normal, -0.4);
			this.dark = get_color_relative (this.normal, -0.1);
			this.light_dark = get_color_relative (this.normal, -0.05);
			this.light_bright = get_color_relative (this.normal, 0.05);
			this.bright = get_color_relative (this.normal, 0.1);
			this.pretty_bright = get_color_relative (this.normal, 0.15);
		}

		public ColorScheme.from_color (Color normal) {
			this.normal = normal;

			// XXX-GEETK
			this.pretty_dark = get_color_relative (this.normal, -0.4);
			this.dark = get_color_relative (this.normal, -0.1);
			this.light_dark = get_color_relative (this.normal, -0.05);
			this.light_bright = get_color_relative (this.normal, 0.05);
			this.bright = get_color_relative (this.normal, 0.1);
			this.pretty_bright = get_color_relative (this.normal, 0.15);
		}

		construct {
			// XXX-GEETK
//			this.pretty_dark = get_color_relative (this.normal, -0.4);
//			this.dark = get_color_relative (this.normal, -0.1);
//			this.light_dark = get_color_relative (this.normal, -0.05);
//			this.light_bright = get_color_relative (this.normal, 0.05);
//			this.bright = get_color_relative (this.normal, 0.1);
//			this.pretty_bright = get_color_relative (this.normal, 0.15);
		}

		public static Color set_alpha_channel (Color c, double alpha) {
			return Color.from_rgba (c.r, c.g, c.b, alpha);
		}

		public static Color get_color_absolute (Color c, double luminance) {
//			double h, s, v;
//			rgb2hsv (c, out h, out s, out v);
//			v = v + (1.7 * (luminance - 1));
//			if (v < 0) {
//				v = 0;
//			} else if (v > 1) {
//				v = 1;
//			}
//			return hsv2rgb (h, s, v);
				
			double h, s, l;
			rgb2hsl (c, out h, out s, out l);
//			double a = (h % 60) / 60;
//			if (a > 0.5) {
//				a -= 0.5;
//			}
//			l = l + (1.7 * (luminance - 1));
			l = luminance;
			if (l < 0) {
				l = 0;
			} else if (l > 1) {
				l = 1;
			}
			return hsl2rgb (h, s, l);
		}

		public static Color get_color_relative (Color c, double luminance) {
			double h, s, l;
			rgb2hsl (c, out h, out s, out l);
//			double a = (h % 60) / 60;
//			if (a > 0.5) {
//				a -= 0.5;
//			}
			l = l + luminance;
			if (l < 0) {
				l = 0;
			} else if (l > 1) {
				l = 1;
			}
			return hsl2rgb (h, s, l);
		}

		private static void rgb2hsl (Color c, out double h, out double s, out double l) {
			double r = c.r;
			double g = c.g;
			double b = c.b;
			double max = double.max (r, double.max (g, b));
			double min = double.min (r, double.min (g, b));
			l = 0.5 * (max + min);
			if (max == min) {
				h = double.NAN;
			} else {
				if (max == r) {
					if (g >= b) {
						h = (g - b) / (max - min) * 60;
					} else {
						h = (g - b) / (max - min) * 60 + 360;
					}
				} else if (max == g) {
					h = (b - r) / (max - min) * 60 + 120;
				} else {
					h = (r - g) / (max - min) * 60 + 240;
				}
			}
			if (0.0000001 <= l && l <= 0.5) {
				s = (max - min) / (2 * l);
			} else if (l > 0.5) {
				s = (max - min) / (2 - 2 * l);
			} else {
				s = 0;
			}
		}

		private static Color hsl2rgb (double h, double s, double l) {
			double r = l;
			double g = l;
			double b = l;
			double q;
			if (l < 0.5) {
				q = l * (1 + s);
			} else {
				q = l + s - l * s;
			}
//			double p = 2 * l - q;		// XXX-GEETK: never used
			if (q > 0) {
				double m = l + l - q;
				h /= 60;
				int i = (int) h % 6;
				double vsf = q * (q - m) / q * (h - i);
				switch (i) {
				case 0:
					r = q;
					g = m + vsf;
					b = m;
					break;
				case 1:
					r = m - vsf;
					g = q;
					b = m;
					break;
				case 2:
					r = m;
					g = q;
					b = m + vsf;
					break;
				case 3:
					r = m;
					g = m - vsf;
					b = q;
					break;
				case 4:
					r = m + vsf;
					g = m;
					b = q;
					break;
				case 5:
					r = q;
					g = m;
					b = m - vsf;
					break;
				}
			}
			return Color.from_rgb (r, g, b);
		}

		// http://www.daniweb.com/techtalkforums/thread38302.html
		// XXX-GEETK: never used
//		private static void rgb2hsv (Color c, out double h, out double s, out double v) {
//			double r = c.r;
//			double g = c.g;
//			double b = c.b;
//			double max = double.max (r, double.max (g, b));
//			double min = double.min (r, double.min (g, b));
//			double delta = max - min;
//			v = max;
//			if (Math.fabs (delta) < 0.0000001) {
//				h = s = 0;
//			} else {
//				s = delta / max;

//				if (r == max) {
//					h = 60.0 * (g - b) / delta;
//				} else if (g == max) {
//					h = 60.0 * (2 + (b - r) / delta);
//				} else {
//					h = 60.0 * (4 + (r - g) / delta);
//				}

//				if (h < 0) {
//					h += 360;
//				} else if (h > 360) {
//					h -= 360;
//				}
//			}
//		}

		// http://en.wikipedia.org/wiki/HSV_color_space
		// XXX-GEETK: never used
//		private static Color hsv2rgb (double h, double s, double v) {
//			int h_i = (int) (h / 60) % 6;
//			double f = h / 60 - h_i;
//			switch (h_i) {
//			case 0:
//				return Color.from_rgb (v, v * (1 - (1 - f) * s), v * (1 - s));
//			case 1:
//				return Color.from_rgb (v * (1 - f * s), v, v * (1 - s));
//			case 2:
//				return Color.from_rgb (v * (1 - s), v, v * (1 - (1 - f) * s));
//			case 3:
//				return Color.from_rgb (v * (1 - s), v * (1 - f * s), v);
//			case 4:
//				return Color.from_rgb (v * (1 - (1 - f) * s), v * (1 - s), v);
//			}
//			return Color.from_rgb (v, v * (1 - s), v * (1 - f * s));
//		}
	}
}

